/**
 * 本质上就是单点修改，然后问：从pos开始，连续的有多少个与pos位置相等的元素
 * 用线段树维护连续段的数量
 * 查询操作，在线段树上做二分即可
 * 也可以对一个贪吃蛇用26个set维护每个字母出现的位置，再做处理，具体没想清楚
 */
#include <bits/stdc++.h>
using namespace std;

#include <bits/extc++.h>
using namespace __gnu_pbds;


struct SegTree{ // 线段树带延迟

using llt = int;

struct _t{
    int cnt;
    int left;
    int right;
    _t(int a=0,int b=0,int c=0):cnt(a),left(b),right(c){}
    bool operator == (const _t & b) const {
        return cnt == b.cnt and left == b.left and right == b.right;
    }
};

using value_type = _t;
vector<value_type> data; // 线段树

using lazy_type = llt;
vector<lazy_type> lazy; // 延迟标记

/// 从下往上计算信息，要变动
value_type _up_(const value_type & ls, const value_type & rs) {
    if(ls == value_zero()) return rs;
    if(rs == value_zero()) return ls;
    return _t(ls.cnt + rs.cnt - (ls.right == rs.left ? 1 : 0), ls.left, rs.right);
}

/// 从上往下计算信息，要变动
void _dn_(int t, int s, int e, const lazy_type & delta) {
    data[t] = {1, delta, delta};
}

/// 初始化，清零，不用动
void init(int n) {
    data.assign(n + 1 << 2, value_zero());
    lazy.assign(n + 1 << 2, lazy_zero());
}

/// 这个函数不用动
void build(int n) {
    _build(1, 1, n);
}

/// 几乎不用动
value_type query(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {
        return data[t];
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    value_type ans = value_zero();
    if(a <= mid) ans = _up_(ans, query(lson(t), s, mid, a, b));
    if(mid < b) ans = _up_(ans, query(rson(t), mid + 1, e, a, b));
    return ans;
}

/// 几乎不用动
void modify(int t, int s, int e, int a, int b, const lazy_type & delta) {
    if(a <= s and e <= b) {
        _dn_(t, s, e, delta);
        return;
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    if(a <= mid) modify(lson(t), s, mid, a, b, delta);
    if(mid < b) modify(rson(t), mid + 1, e, a, b, delta);
    _pushUp(t);
    return;
}

/// 这个函数不用动
void _pushUp(int t) {
    data[t] = _up_(data[lson(t)], data[rson(t)]);
}

/// 这个函数几乎不用动
void _pushDown(int t, int s, int e) {
    if(lazy_zero() == lazy[t]) return;
    auto & lz = lazy[t];
    auto ls = lson(t), rs = rson(t);
    int mid = (s + e) >> 1;

    _dn_(ls, s, mid, lz);
    _dn_(rs, mid + 1, e, lz);

    lz = lazy_zero();
}


/// 几乎不用动
void _build(int t, int s, int e) {
    if(s == e) {
        assert(0);
        return; 
    }
    int mid = (s + e) >> 1;
    _build(lson(t), s, mid);
    _build(rson(t), mid + 1, e);
    _pushUp(t);
}

/// 辅助函数，视延迟的类型而变动
static const lazy_type & lazy_zero() {
    static const lazy_type LAZY0 = 0;
    return LAZY0; 
}

/// 辅助函数，视线段树信息类型而变动
static const value_type & value_zero() {
    static const value_type VALUE0 = {0, 0, 0};
    return VALUE0;
}

/// 这两个函数不用变动
static int lson(int x) {return x << 1;}
static int rson(int x) {return lson(x) | 1;}


}StA, StB;


using Real = long double;
using llt = long long;
using pii = pair<int, int>;
using vi = vector<int>;
using vvi = vector<vi>;

int N, M;
vector<vector<char>> Board;
vvi IdxA, IdxB;

void init(){
    IdxA.assign(N + 1, vi(M + 1, 0));
    int limit = N * M;
    int k = 0;

    StA.init(limit);
    for(int i=1;i<=N;i+=2){
        for(int j=1;j<=M;++j){
            IdxA[i][j] = ++k;
            StA.modify(1, 1, limit, k, k, Board[i][j]);
        }
        if(i + 1 <= N)for(int j=M;j>=1;--j){
            IdxA[i+1][j] = ++k;
            StA.modify(1, 1, limit, k, k, Board[i+1][j]);
        }
    }
    
    IdxB.assign(N + 1, vi(M + 1, 0));
    StB.init(limit);
    k = 0;

    for(int j=1;j<=M;j+=2){
        for(int i=1;i<=N;++i){
            IdxB[i][j] = ++k;
            StB.modify(1, 1, limit, k, k, Board[i][j]);
        }
        if(j + 1 <= M)for(int i=N;i>=1;--i){
            IdxB[i][j + 1] = ++k;
            StB.modify(1, 1, limit, k, k, Board[i][j + 1]);
        }
    }

    return;
}

int Q;
int Limit;

int proc(SegTree & st, int pos){
    int left = pos, right = Limit, mid;
    do{
        mid = (left + right) >> 1;
        if(st.query(1, 1, Limit, pos, mid).cnt == 1) left = mid + 1;
        else right = mid - 1;
    }while(left <= right);
    return right - pos + 1;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(0);
    int nofkase = 1;
    // cin >> nofkase;
    while(nofkase--){
        cin >> N >> M;
        Board.assign(N+1, vector<char>(M+1, '\0'));
        for(int i=1;i<=N;++i)for(int j=1;j<=M;++j)cin>>Board[i][j];
    
        init();
        int limit = N * M;
        Limit = limit;

        cin >> Q;
        char ch;
        for(int cmd, x, y, q=1;q<=Q;++q){
            cin >> cmd >> x >> y;
            if(1 == cmd){
                cin >> ch; 
                int pos = IdxA[x][y];
                StA.modify(1, 1, limit, pos, pos, ch);
                pos = IdxB[x][y];
                StB.modify(1, 1, limit, pos, pos, ch);
            }else if(2 == cmd){
                cout << proc(StA, IdxA[x][y]) << endl;
            }else if(3 == cmd){
                cout << proc(StB, IdxB[x][y]) << endl;
            }else{
                assert(0);
            }
        }
    }
    return 0;
}